import 'dart:core';

import 'package:flutter/services.dart';

import '../public/util/log_util.dart';
import 'flutter_sound_platform_interface.dart';
import 'flutter_sound_player_platform_interface.dart';

const MethodChannel _channel =
    MethodChannel('com.dooboolab.flutter_sound_player');

/// An implementation of [FlutterSoundPlayerPlatform] that uses method channels.
class MethodChannelFlutterSoundPlayer extends FlutterSoundPlayerPlatform {
  /* ctor */ MethodChannelFlutterSoundPlayer() {
    setCallback();
  }

  void setCallback() {
    //_channel = const MethodChannel('com.dooboolab.flutter_sound_player');
    _channel.setMethodCallHandler((MethodCall call) {
      return channelMethodCallHandler(call)!;
    });
  }

  Future<dynamic>? channelMethodCallHandler(MethodCall call) {
    var aPlayer = getSession(call.arguments!['slotNo'] as int);
    Map arg = call.arguments;

    var success = call.arguments['success'] != null
        ? call.arguments['success'] as bool
        : false;
    if (arg['state'] != null) aPlayer.updatePlaybackState(arg['state']);

    switch (call.method) {
      case 'updateProgress':
        {
          aPlayer.updateProgress(
              duration: arg['duration'], position: arg['position']);
        }
        break;

      case 'needSomeFood':
        {
          aPlayer.needSomeFood(arg['arg']);
        }
        break;

      case 'audioPlayerFinishedPlaying':
        {
          aPlayer.audioPlayerFinished(arg['arg']);
        }
        break;

      case 'updatePlaybackState':
        {
          aPlayer.updatePlaybackState(arg['arg']);
        }
        break;

      case 'openPlayerCompleted':
        {
          aPlayer.openPlayerCompleted(call.arguments['state'], success);
        }
        break;

      case 'startPlayerCompleted':
        {
          var duration = arg['duration'] as int;
          aPlayer.startPlayerCompleted(
              call.arguments['state'], success, duration);
        }
        break;

      case 'stopPlayerCompleted':
        {
          aPlayer.stopPlayerCompleted(call.arguments['state'], success);
        }
        break;

      case 'pausePlayerCompleted':
        {
          aPlayer.pausePlayerCompleted(call.arguments['state'], success);
        }
        break;

      case 'resumePlayerCompleted':
        {
          aPlayer.resumePlayerCompleted(call.arguments['state'], success);
        }
        break;

      case 'closePlayerCompleted':
        {
          aPlayer.closePlayerCompleted(call.arguments['state'], success);
        }
        break;

      case 'log':
        {
          log(call.arguments['msg']);
        }
        break;

      default:
        throw ArgumentError('Unknown method ${call.method}');
    }

    return null;
  }

//===============================================================================================================================

  Future<int> invokeMethod(FlutterSoundPlayerCallback callback,
      String methodName, Map<String, dynamic> call) async {
    call['slotNo'] = findSession(callback);
    return await _channel.invokeMethod(methodName, call) as int;
  }

  Future<String> invokeMethodString(FlutterSoundPlayerCallback callback,
      String methodName, Map<String, dynamic> call) async {
    call['slotNo'] = findSession(callback);
    return await _channel.invokeMethod(methodName, call) as String;
  }

  Future<bool> invokeMethodBool(FlutterSoundPlayerCallback callback,
      String methodName, Map<String, dynamic> call) async {
    call['slotNo'] = findSession(callback);
    return await _channel.invokeMethod(methodName, call) as bool;
  }

  Future<Map> invokeMethodMap(FlutterSoundPlayerCallback callback,
      String methodName, Map<String, dynamic> call) async {
    call['slotNo'] = findSession(callback);
    var r = await _channel.invokeMethod(methodName, call);
    return r;
  }

  @override
  Future<void>? resetPlugin(
    FlutterSoundPlayerCallback callback,
  ) {
    return _channel.invokeMethod(
      'resetPlugin',
    );
  }

  @override
  Future<int> openPlayer(FlutterSoundPlayerCallback callback,
      {bool voiceProcessing = false}) {
    return invokeMethod(
      callback,
      'openPlayer',
      {'voiceProcessing': voiceProcessing},
    );
  }

  @override
  Future<int> closePlayer(
    FlutterSoundPlayerCallback callback,
  ) {
    return invokeMethod(
      callback,
      'closePlayer',
      <String, dynamic>{},
    );
  }

  @override
  Future<int> getPlayerState(
    FlutterSoundPlayerCallback callback,
  ) {
    return invokeMethod(
      callback,
      'getPlayerState',
      <String, dynamic>{},
    );
  }

  @override
  Future<Map<String, Duration>> getProgress(
    FlutterSoundPlayerCallback callback,
  ) async {
    var m2 = await invokeMethodMap(
      callback,
      'getProgress',
      <String, dynamic>{},
    );
    var r = <String, Duration>{
      'duration': Duration(milliseconds: m2['duration']!),
      'progress': Duration(milliseconds: m2['position']!),
    };
    return r;
  }

  @override
  Future<bool> isDecoderSupported(
    FlutterSoundPlayerCallback callback, {
    AudioCodec codec = AudioCodec.defaultCodec,
  }) {
    return invokeMethodBool(
      callback,
      'isDecoderSupported',
      {
        'codec': codec.index,
      },
    );
  }

  @override
  Future<int> setSubscriptionDuration(
    FlutterSoundPlayerCallback callback, {
    Duration? duration,
  }) {
    return invokeMethod(
      callback,
      'setSubscriptionDuration',
      {'duration': duration!.inMilliseconds},
    );
  }

  @override
  Future<int> startPlayer(FlutterSoundPlayerCallback callback,
      {AudioCodec? codec,
      Uint8List? fromDataBuffer,
      String? fromURI,
      int? numChannels,
      int? sampleRate}) {
    return invokeMethod(
      callback,
      'startPlayer',
      {
        'codec': codec!.index,
        'fromDataBuffer': fromDataBuffer,
        'fromURI': fromURI,
        'numChannels': numChannels,
        'sampleRate': sampleRate
      },
    );
  }

  @override
  Future<int> startPlayerFromMic(FlutterSoundPlayerCallback callback,
      {int? numChannels, int? sampleRate}) {
    return invokeMethod(
      callback,
      'startPlayerFromMic',
      {
        'numChannels': numChannels,
        'sampleRate': sampleRate,
      },
    );
  }

  @override
  Future<int> feed(
    FlutterSoundPlayerCallback callback, {
    Uint8List? data,
  }) {
    return invokeMethod(
      callback,
      'feed',
      {
        'data': data,
      },
    );
  }

  @override
  Future<int> stopPlayer(
    FlutterSoundPlayerCallback callback,
  ) {
    return invokeMethod(
      callback,
      'stopPlayer',
      <String, dynamic>{},
    );
  }

  @override
  Future<int> pausePlayer(
    FlutterSoundPlayerCallback callback,
  ) {
    return invokeMethod(
      callback,
      'pausePlayer',
      <String, dynamic>{},
    );
  }

  @override
  Future<int> resumePlayer(
    FlutterSoundPlayerCallback callback,
  ) {
    return invokeMethod(
      callback,
      'resumePlayer',
      <String, dynamic>{},
    );
  }

  @override
  Future<int> seekToPlayer(FlutterSoundPlayerCallback callback,
      {Duration? duration}) {
    return invokeMethod(
      callback,
      'seekToPlayer',
      {
        'duration': duration!.inMilliseconds,
      },
    );
  }

  @override
  Future<int> setVolume(FlutterSoundPlayerCallback callback, {double? volume}) {
    return invokeMethod(callback, 'setVolume', {
      'volume': volume,
    });
  }

  @override
  Future<int> setSpeed(FlutterSoundPlayerCallback callback,
      {required double speed}) {
    return invokeMethod(callback, 'setSpeed', {
      'speed': speed,
    });
  }

  @override
  Future<String> getResourcePath(
    FlutterSoundPlayerCallback callback,
  ) {
    return invokeMethodString(
      callback,
      'getResourcePath',
      <String, dynamic>{},
    );
  }
}
